package com.mec.ServerCluser.core;

import com.mec.ServerCluser.Exception.SameAppServerAndServiceRegistryException;
import com.mec.ServerCluser.Interface.IAppUserClient;
import com.mec.ServerCluser.RPC.RpcServer;
import com.mec.util.core.ConfigReader;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 集群服务器注册中心
 *  功能：
 *  1、接收应用服务器的注册请求,并与其保持长连接。及时对数据进行维护
 *
 *  2、处理应用服务器的正常或者异常下线，及时对数据进行维护。
 *
 *  3、响应客户端的“更新服务列表请求”。在接收到请求后，所有服务信息与其下的应用服务器发送给客户端。
 *
 *  4、定时对应用服务器进行心跳检测：定时检测应用服务器的响应速度。
 *
 *       其中具体方式是在定时的时间间隔后，向应用服务器发送一个测试请求，并开始计时。在客户端接收到测试命令消息后即刻
 *  向服务器返回测试成功的消息。服务器端在收到消息后立即停止计时，将时间间隔计算出来。如此操作三次（次数可以从配置文件进行配置）。
 *  计算出相应的平均值，并将其处理成可以代表响应速度的值或者几个等级。并将此值注入到该应用服务器对象的time成员中。
 */
public class RegistryCenter implements Runnable {
    private ServerSocket appServerSocket;
    private RpcServer appUserSocker;
    private static int appServerSocketPort;
    private static int appUserSockerPort;
    private volatile boolean continueListeningAppServerRequest;
    private static final Map<String, List<AppServerDefination>> serviceMap;

    static {
        //保证线程安全的HashMap
        serviceMap = new ConcurrentHashMap<>();
        ConfigReader.init("./net.config.properties");
        appServerSocketPort =
                Integer.valueOf(ConfigReader.getValue("registryCenterAppServerClientPort"));
        appUserSockerPort =
                Integer.valueOf(ConfigReader.getValue("registryCenterAppUserClientPort"));
    }

    public RegistryCenter() {
    }

    /**
     * 将注册中心启动起来
     * 启动监听应用服务器线程
     * 启动监听客户端连接线程
     */
    public void startup() {

        try {
            appServerSocket = new ServerSocket(appServerSocketPort);
            continueListeningAppServerRequest = true;
            synchronized (RegistryCenter.class) {
                new Thread(this).start();
                RegistryCenter.class.wait();
            }
            System.out.println("appServerClient服务器启动成功！");

            appUserSocker = new RpcServer();
            appUserSocker.setPort(appUserSockerPort);
            appUserSocker.registryRpc(IAppUserClient.class, AppUserClientService.class);
            appUserSocker.startRpcServer();
            System.out.println("appUserClient服务器启动成功！");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            System.out.println("客户端rpc服务器启动失败！");
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        synchronized (RegistryCenter.class) {
            RegistryCenter.class.notify();
        }

        while(continueListeningAppServerRequest) {
            Socket appServer;
            try {
                appServer = appServerSocket.accept();
                new RegistryAppServerConversation(this, appServer);
            } catch (IOException e) {
                continueListeningAppServerRequest = false;
                System.out.println("AppServer服务器已停止。");
            }
        }
        closeAppServerSocket();

    }

    private void closeAppUserServer() {
        appUserSocker.stopRpcServer();
        System.out.println("appUser服务器已停止。");
    }

    private void closeAppServerSocket() {
        try {
            if(appServerSocket != null && !appServerSocket.isClosed()) {
                appServerSocket.close();
            }
            appServerSocket = null;
        } catch (IOException e) {
            System.out.println("你倒了大霉了。");
            e.printStackTrace();
        }
    }

    public void closeCenter() {
        continueListeningAppServerRequest = false;
        closeAppUserServer();
        closeAppServerSocket();
    }

    public synchronized void addServer(String serviceName, String appServerIp, int appServerSocketPort)
            throws SameAppServerAndServiceRegistryException {
        List<AppServerDefination> serviceServerList = serviceMap.get(serviceName);
        AppServerDefination asd;
        if(serviceServerList == null) {
            //创建新的serviceName
            asd = new AppServerDefination(appServerIp, appServerSocketPort);
            serviceServerList = new ArrayList<>();
            serviceServerList.add(asd);
            serviceMap.put(serviceName, serviceServerList);
            System.out.println("成功添加：[" + asd +"]");
            return;
        }
        asd = new AppServerDefination(appServerIp, appServerSocketPort);
        boolean repeat = false;
        for(AppServerDefination asdTmp : serviceServerList) {
            repeat = asdTmp.equals(asd);
        }
        if(!repeat) {
            serviceServerList.add(asd);
            System.out.println("成功添加：[" + asd +"]");
        } else{
            throw new SameAppServerAndServiceRegistryException("禁止同一服务器重复注册！");
        }
    }

    public synchronized void removeServer(String serviceName, String appServerIp, int appServerSocketPort) throws Exception {
        List<AppServerDefination> serviceServerList = serviceMap.get(serviceName);
        AppServerDefination asd;
        if(serviceServerList == null) {
            return;
        }
        asd = new AppServerDefination(appServerIp, appServerSocketPort);
//        for(AppServerDefination asdTmp : serviceServerList) {
//            if(asdTmp.equals(asd)) {
//                serviceServerList.remove(asdTmp);
//                return;
//            }
//        }
//        throw new Exception("要删除的服务器不存在！");
        serviceServerList.remove(asd);
        System.out.println("检测下线请求到并删除应用服务器：[" + asd + "]");
    }

    static Map<String, List<AppServerDefination>> getServiceMap() {
        return serviceMap;
    }
}
